package org.zero.common.core.util.quartz;

import lombok.Setter;
import lombok.extern.java.Log;
import org.quartz.CronScheduleBuilder;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.ScheduleBuilder;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.quartz.impl.StdSchedulerFactory;
import org.zero.common.core.support.context.spring.SpringUtils;

import java.util.Date;
import java.util.Objects;
import java.util.logging.Level;

/**
 * @author zero
 * @since 2021/10/21
 */
@Log
public class QuartzUtils {
    public static boolean scheduleJob(String key, Class<? extends Job> clazz, String cron) {
        return scheduleJob(key, clazz, cron, new JobDataMap());
    }

    public static boolean scheduleJob(String key, Class<? extends Job> clazz, String cron, JobDataMap jobDataMap) {
        return scheduleJob(key, clazz, createTrigger(key, cron), jobDataMap);
    }

    public static boolean scheduleJob(String key, Class<? extends Job> clazz, Trigger trigger) {
        return scheduleJob(key, clazz, trigger, new JobDataMap());
    }

    public static boolean scheduleJob(String key, Class<? extends Job> clazz, Trigger trigger, JobDataMap jobDataMap) {
        return scheduleJob(createJobDetail(key, clazz, jobDataMap), trigger);
    }

    public static boolean scheduleJob(Trigger trigger) {
        try {
            getScheduler().scheduleJob(trigger);
            return true;
        } catch (SchedulerException e) {
            log.log(Level.WARNING, String.format("schedule job error: %s", trigger.getJobKey()), e);
            return false;
        }
    }

    /**
     * 自动执行 job
     */
    public static boolean scheduleJob(JobDetail jobDetail, Trigger trigger) {
        try {
            getScheduler().scheduleJob(jobDetail, trigger);
            return true;
        } catch (SchedulerException e) {
            log.log(Level.WARNING, String.format("schedule job error: %s", jobDetail.getKey()), e);
            return false;
        }
    }

    public static boolean rescheduleJob(String key, String cron) {
        return rescheduleJob(createTriggerKey(key), createTrigger(key, cron));
    }

    /**
     * 重新调度 job
     */
    public static boolean rescheduleJob(TriggerKey triggerKey, Trigger trigger) {
        try {
            getScheduler().rescheduleJob(triggerKey, trigger);
            return true;
        } catch (SchedulerException e) {
            log.log(Level.WARNING, String.format("reschedule job error: %s", trigger.getJobKey()), e);
            return false;
        }
    }

    public static boolean triggerJob(String key) {
        return triggerJob(key, new JobDataMap());
    }

    public static boolean triggerJob(String key, JobDataMap jobDataMap) {
        return triggerJob(createJobKey(key), jobDataMap);
    }

    /**
     * 触发执行 job
     */
    public static boolean triggerJob(JobKey jobKey, JobDataMap jobDataMap) {
        try {
            getScheduler().triggerJob(jobKey, jobDataMap);
            return true;
        } catch (SchedulerException e) {
            log.log(Level.WARNING, String.format("trigger job error: %s", jobKey), e);
            return false;
        }
    }

    public static boolean addJob(String key, Class<? extends Job> clazz) {
        return addJob(key, clazz, new JobDataMap());
    }

    public static boolean addJob(String key, Class<? extends Job> clazz, JobDataMap jobDataMap) {
        return addJob(createJobDetail(key, clazz, jobDataMap), true);
    }

    /**
     * 添加 job
     */
    public static boolean addJob(JobDetail jobDetail, boolean replace) {
        try {
            getScheduler().addJob(jobDetail, replace);
            return true;
        } catch (SchedulerException e) {
            log.log(Level.WARNING, String.format("add job error: %s", jobDetail.getKey()), e);
            return false;
        }
    }

    public static boolean checkExists(String key) {
        return checkExists(createJobKey(key));
    }

    /**
     * 检查 job 是否存在
     */
    public static boolean checkExists(JobKey jobKey) {
        try {
            return getScheduler().checkExists(jobKey);
        } catch (SchedulerException e) {
            log.log(Level.WARNING, String.format("check job exists error: %s", jobKey), e);
            return false;
        }
    }

    public static boolean deleteJob(String key) {
        return deleteJob(createJobKey(key));
    }

    /**
     * 删除 job
     */
    public static boolean deleteJob(JobKey jobKey) {
        try {
            return getScheduler().deleteJob(jobKey);
        } catch (SchedulerException e) {
            log.log(Level.WARNING, String.format("delete job error: %s", jobKey), e);
            return false;
        }
    }

    public static boolean pauseJob(String key) {
        return pauseJob(createJobKey(key));
    }

    /**
     * 暂停 job
     *
     * @see QuartzUtils#resumeJob(JobKey)
     */
    public static boolean pauseJob(JobKey jobKey) {
        try {
            getScheduler().pauseJob(jobKey);
            return true;
        } catch (SchedulerException e) {
            log.log(Level.WARNING, String.format("pause job error: %s", jobKey), e);
            return false;
        }
    }

    public static boolean resumeJob(String key) {
        return resumeJob(createJobKey(key));
    }

    /**
     * 恢复 job
     *
     * @see QuartzUtils#pauseJob(JobKey)
     */
    public static boolean resumeJob(JobKey jobKey) {
        try {
            getScheduler().resumeJob(jobKey);
            return true;
        } catch (SchedulerException e) {
            log.log(Level.WARNING, String.format("resume job error: %s", jobKey), e);
            return false;
        }
    }

    public static boolean interruptJob(String key) {
        return interruptJob(createJobKey(key));
    }

    /**
     * 中断 job
     */
    public static boolean interruptJob(JobKey jobKey) {
        try {
            getScheduler().standby();
            return getScheduler().interrupt(jobKey);
        } catch (SchedulerException e) {
            log.log(Level.WARNING, String.format("interrupt job error: %s", jobKey), e);
            return false;
        }
    }

    /* ********************************************* creating-related Methods ********************************************* */

    public static JobDetail createJobDetail(String key, Class<? extends Job> clazz) {
        return createJobDetail(key, clazz, new JobDataMap());
    }

    public static JobDetail createJobDetail(String key, Class<? extends Job> clazz, JobDataMap jobDataMap) {
        return createJobDetail(createJobKey(key), clazz, jobDataMap);
    }

    public static JobDetail createJobDetail(JobKey jobKey, Class<? extends Job> clazz, JobDataMap jobDataMap) {
        return JobBuilder.newJob()
                .ofType(clazz)
                .withIdentity(jobKey)
                // 使用给定描述
                // .withDescription("")
                .usingJobData(jobDataMap)
                // 调度器执行任务时，遇到'recovery'（恢复）或者'fail-over'（故障转移）重新执行
                .requestRecovery()
                // 持久化：没有触发器也不删除该任务
                .storeDurably()
                .build();
    }

    public static Trigger createTrigger(String key, String cron) {
        return createTrigger(key, cron, new JobDataMap());
    }

    public static Trigger createTrigger(String key, String cron, JobDataMap jobDataMap) {
        return createTrigger(createTriggerKey(key), CronScheduleBuilder.cronSchedule(cron), jobDataMap, null);
    }

    public static Trigger createTrigger(TriggerKey triggerKey,
                                        ScheduleBuilder<? extends Trigger> scheduleBuilder,
                                        JobDataMap jobDataMap,
                                        JobKey jobKey) {
        return createTrigger(triggerKey, scheduleBuilder, jobDataMap, new Date(), null, jobKey);
    }

    public static Trigger createTrigger(TriggerKey triggerKey,
                                        ScheduleBuilder<? extends Trigger> scheduleBuilder,
                                        JobDataMap jobDataMap,
                                        Date startDate, Date endDate,
                                        JobKey jobKey) {
        return TriggerBuilder.newTrigger()
                .withIdentity(triggerKey)
                .withSchedule(scheduleBuilder)
                // 使用给定描述
                // .withDescription("")
                // 线程优先级
                .withPriority(Trigger.DEFAULT_PRIORITY)
                // 在指定时间开始执行
                .startAt(startDate)
                // 在指定时间结束执行
                .endAt(endDate)
                .usingJobData(jobDataMap)
                // 关联到指定 job
                .forJob(jobKey)
                .build();
    }

    public static JobKey createJobKey(String key) {
        return new JobKey(key + JOB_SUFFIX, key + JOB_GROUP_SUFFIX);
    }

    public static JobKey createJobKey(String jobKey, String groupKey) {
        return new JobKey(jobKey + JOB_SUFFIX, groupKey + JOB_SUFFIX);
    }

    public static TriggerKey createTriggerKey(String key) {
        return new TriggerKey(key + TRIGGER_SUFFIX, key + TRIGGER_GROUP_SUFFIX);
    }

    public static TriggerKey createTriggerKey(String triggerKey, String groupKey) {
        return new TriggerKey(triggerKey + TRIGGER_SUFFIX, groupKey + TRIGGER_SUFFIX);
    }

    /* **************************************************** Other **************************************************** */
    protected static final String GROUP_SUFFIX = "-group";
    protected static final String JOB_SUFFIX = "-job";
    protected static final String JOB_GROUP_SUFFIX = JOB_SUFFIX + GROUP_SUFFIX;
    protected static final String TRIGGER_SUFFIX = "-trigger";
    protected static final String TRIGGER_GROUP_SUFFIX = TRIGGER_SUFFIX + GROUP_SUFFIX;

    @Setter
    protected static volatile Scheduler scheduler;

    public static Scheduler getScheduler() {
        checkAndCreate();
        return scheduler;
    }

    protected static void checkAndCreate() {
        if (Objects.isNull(scheduler)) {
            synchronized (QuartzUtils.class) {
                if (Objects.isNull(scheduler)) {
                    scheduler = SpringUtils.getBeanProvider(Scheduler.class).getIfAvailable();
                }
                if (Objects.isNull(scheduler)) {
                    try {
                        scheduler = StdSchedulerFactory.getDefaultScheduler();
                    } catch (SchedulerException ignored) {
                    }
                }
            }
        }
    }

    protected QuartzUtils() {
        throw new UnsupportedOperationException();
    }
}
